package com.cynteka.cyfoman.core;

import com.cynteka.cyfoman.core.config.*;
import com.sun.xml.internal.bind.v2.model.core.ID;
import org.apache.commons.chain.Catalog;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import javax.sql.DataSource;
import javax.swing.*;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * User: Sergey
 * Date: 25.07.11
 * Time: 15:11
 */
public abstract class Util {

    private static final String PARAM_RE = "\\$\\{([^}]*)\\}";
    private static final Pattern PARAM_PATTERN = Pattern.compile(PARAM_RE);

    public static List<String> getGroups(Matcher m) {
        List<String> res = new ArrayList<String>(m.groupCount());
        for (int i = 0; i < m.groupCount(); i++) {
            res.add(m.group(i + 1));
        }
        return res;
    }

    public static List<String> findAllParams(String query, List<String> values, List<String> varNames) {
        Matcher paramMatcher = PARAM_PATTERN.matcher(query);

        List<String> res = new ArrayList<String>();
        while (paramMatcher.find()) {
            String paramName = paramMatcher.group(1);
            int idx = varNames.indexOf(paramName);
            if (idx < 0) {
                throw new IllegalArgumentException("Can not find variable : " + paramName);
            }
            String value = values.get(idx);
            res.add(value);
        }
        return res;
    }

    public static String processQuery(String query) {
        return substituteAllParams(query, "?");
    }

    public static String substituteAllParams(String s, String value) {
        s = s.replaceAll(PARAM_RE, value);
        return s;

    }

    public static String substituteParams(String s, Object paramName, Object value) {
        String findRE = "\\$\\{" + paramName + "\\}";
        try {
            String replacement = Matcher.quoteReplacement(String.valueOf(value));
            s = s.replaceAll(findRE, replacement);
        } catch (IllegalArgumentException e) {
            throw e;
        }
        return s;
    }


    public static String substituteParams(String s, ValueObject valueObject) {
        throw new IllegalStateException("Not implemented yet");
    }
    /*
    public static <ID, P> String substituteParams(String s, ValueObject<ID, P> valueObject) {

        DecimalFormat[] dfs = new DecimalFormat[16];
        StringBuffer sb = new StringBuffer();
        for (int j = 0; j < dfs.length; j++) {
            sb.append('0');
            DecimalFormat df = new DecimalFormat(sb.toString());
            dfs[j] = df;
        }

        for (P param : valueObject.getParams()) {

            Object value = valueObject.getParam(param);
            s = Util.substituteParams(s, param.getId(), String.valueOf(value));

            if (value instanceof Integer) {

                Integer i = (Integer) value;

                sb = new StringBuffer();
                for (DecimalFormat df : dfs) {
                    sb.append('0');
                    s = Util.substituteParams(s, param.getId() + "." + sb, df.format(i));
                }
            } else if (value instanceof Date) {
                Date d = (Date) value;
                Calendar cal = Calendar.getInstance();
                cal.setTime(d);
                s = Util.substituteParams(s, param.getId() + ".year", dfs[3].format(cal.get(Calendar.YEAR)));
                s = Util.substituteParams(s, param.getId() + ".month", dfs[1].format(cal.get(Calendar.MONTH) + 1));
                s = Util.substituteParams(s, param.getId() + ".day", dfs[1].format(cal.get(Calendar.DAY_OF_MONTH)));
                s = Util.substituteParams(s, param.getId() + ".hour", dfs[1].format(cal.get(Calendar.HOUR_OF_DAY)));
                s = Util.substituteParams(s, param.getId() + ".min", dfs[1].format(cal.get(Calendar.MINUTE)));
                s = Util.substituteParams(s, param.getId() + ".sec", dfs[1].format(cal.get(Calendar.SECOND)));
            }
        }
        return s;
    }
    */

    public static ValueObject populateObject(final ActionMD actionMD, Object id, DataSource ds) {
        final JdbcTemplate template = new JdbcTemplate(ds);
        final ValueObject res = new ValueObject();
        res.setId(id);
        final String tableName = actionMD.getTable();
        final RowMapper<Object> resultSetExtractor = new RowMapper<Object>() {
            public Object mapRow(ResultSet resultSet, int row) throws SQLException {
                for (ParamMD param : actionMD.getActionParams()) {
                    if (param.getParamType() != ParamType.COLLECTION) {
                        String colName = param.getId();
                        Object value = resultSet.getObject(colName);
                        res.setParam(param, value);
                    }
                }
                return res;
            }
        };
        template.query("select * from " + tableName + " where id = ?", new Object[]{id}, resultSetExtractor);

        for (ParamMD param : actionMD.getActionParams()) {
            if (param.getParamType() == ParamType.COLLECTION) {
                CollectionMD collectionMD = param.getCollection();
                String query = String.format("select %1s from %2s where %3s = ?", collectionMD.getReferenceToField(), collectionMD.getCrossTable(), collectionMD.getReferenceFromField());
                final List<RefItem> refItems = new ArrayList<RefItem>();
                RowMapper<Object> rowMapper = new RowMapper<Object>() {
                    @Override
                    public Object mapRow(ResultSet resultSet, int i) throws SQLException {
                        RefItem refItem = new RefItem(resultSet.getInt(1), null);
                        refItems.add(refItem);
                        return refItem;
                    }
                };
                template.query(query, rowMapper, id);
                res.setParam(param, refItems);
            }
        }

        return res;
    }

    public static String generateInsertQuery(ActionMD actionMD) {
        StringBuffer fieldNames = new StringBuffer();
        StringBuffer values = new StringBuffer();
        boolean isFirst = true;
        for (ParamMD param : actionMD.getActionParams()) {
            if (param.getParamType() == ParamType.COLLECTION) {
                break;
            }
            if (!isFirst) {
                fieldNames.append(", ");
                values.append(", ");
            }
            isFirst = false;
            fieldNames.append(param.getId());
            values.append("?");

        }
        return "insert into " + actionMD.getTable() + "( " + fieldNames + " ) values ( " + values + ")";
    }

    public static String generateUpdateQuery(ActionMD actionMD) {
        StringBuffer fieldNames = new StringBuffer();
        boolean isFirst = true;
        for (ParamMD param : actionMD.getActionParams()) {
            if (param.getParamType() == ParamType.COLLECTION) {
                break;
            }
            if (!isFirst) {
                fieldNames.append(", ");
            }
            isFirst = false;
            fieldNames.append(param.getId());
            fieldNames.append(" = ?");
        }
        return "update " + actionMD.getTable() + " set " + fieldNames + " where id = ?";
    }

    public static Icon loadIcon(File queryFolder, String icon) {
        Icon res = null;
        if (queryFolder != null && icon != null) {
            File iconFile = new File(queryFolder, icon);
            if (iconFile.exists()) {
                String fileName = iconFile.getAbsolutePath();
                res = new ImageIcon(fileName);
            }
        }

        return res;
    }

    public static String getRelationName(File f, File baseFolder) {
        return getRelationName(f, baseFolder, File.separator);
    }

    public static String getRelationName(File f, File baseFolder, String separator) {
        StringBuilder sb = new StringBuilder();
        File fIter = f;
        while (fIter != null && !fIter.equals(baseFolder)) {
            if (fIter != f) {
                sb.insert(0, separator);
            }
            sb.insert(0, fIter.getName());
            fIter = fIter.getParentFile();
        }
        return sb.toString();
    }

    public static Catalog getCatalog(BeanFactory beanFactory) {
        Catalog catalog = null;
        if(catalog == null) {
            if(beanFactory != null) {
                catalog = (Catalog) beanFactory.getBean(Catalog.CATALOG_KEY);
            }
        }
        return catalog;
    }
}
